package cn.hy.batch;


import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

// Spring Batch默认在发生错误时，任务会被停止。
@Configuration
@Slf4j
public class ErrorRetryDemo {

    //注入任务对象工厂
   @Resource
    private JobBuilderFactory jobBuilderFactory;

    //任务的执行由Step决定,注入step对象的factory
   @Resource
    private StepBuilderFactory stepBuilderFactory;

    //创建Job对象
    @Bean
    public Job errorRetryJobDemo() {
        return jobBuilderFactory.get("error_retry_job_demo")
                .start(error_retry_step1())
                .build();
    }

    //创建Step对象
    @Bean
    public Step error_retry_step1() {
        return stepBuilderFactory.get("error_retry_step1")
                .<String, Integer>chunk(2) //表示每一次处理reader两条,reader/process/write
                .reader(read()) //读取
                .processor(processor()) //处理
                .writer(write()) //写入
                .faultTolerant() //开启容错
                .retry(RuntimeException.class) //捕获的异常,这个异常可以在reader,processor,writer抛出，重试针对的是异常
                .retryLimit(5) //针对捕获的有异常进行重试
                .build();
    }

    public ItemReader<String> read() { //一组字符串，字符内容包含数字和字母
        return new ListItemReader<String>(Arrays.asList("a3", "b5", "cbd7", "f9", "x10"));
    }

    public ItemProcessor<? super String, ? extends Integer> processor() {
        return new ItemProcessor<String, Integer>() {  //将字符串中的字母去掉，返回int数值
            private int retry;

            public Integer process(String s) throws Exception {
                Integer i = Integer.parseInt(s.replaceAll("[a-zA-Z]", ""));

                if (i % 5 == 0) {
                    retry++;
                    log.warn("模拟错误字符串:" + s);
                    log.warn("重试第 " + retry + " 次");

                    if (retry == 0 || retry % 3 != 0) { //调用第3,6,9次成功
                        log.error("发生了错误:" + retry);
                        throw new RuntimeException("处理数据错误" + s);
                    }
                }

                log.info("字符串{} 转换{}", s, i);
                return i;
            }
        };
    }

    public ItemWriter<Integer> write() {
        return new ItemWriter<Integer>() { //打印处理完的内容
            @Override
            public void write(List<? extends Integer> list) throws Exception {
                log.info("写入数据:" + list);
            }
        };
    }
}
